#include "Common.h"
#include "Command.h"
#include "TcpClient.h"
#include "TcpServer.h"
#include "NetworkManager.h"

using namespace ENet;

TcpClient::TcpClient()
	: mClientHost(NULL)
	, mCurPeer(NULL)
	, mConnPeer(NULL)
	, mBandWidthLimit(0)
	, mClientNum(0)
	, mAccept(false)
{
}

TcpClient::~TcpClient()
{
	CleanUp();
}

bool TcpClient::Connect(const char* server, int port, const char* passwd)
{
	if (mConnPeer != NULL)
		AbortConnect();

	// create the host.
	mClientHost = enet_host_create(NULL, 2, NUMCHANNELS, mBandWidthLimit, mBandWidthLimit);
	if (mClientHost == NULL)
		return false;

	// update the address.
    ENetAddress address = { ENET_HOST_ANY, ENET_PORT_ANY };
    enet_address_set_host(&address, server);
    address.port = port;

	// connect to the server.
	mConnPeer = enet_host_connect(mClientHost, &address, NUMCHANNELS, 0); 
	enet_host_flush(mClientHost);

	return true;
}

void TcpClient::Disconnect(bool async, bool cleanup)
{
	if (mCurPeer) 
	{
		enet_peer_disconnect(mCurPeer, DISC_NONE);
		enet_host_flush(mClientHost);

		if (mCurPeer->state != ENET_PEER_STATE_DISCONNECTED)
		{
			if (async) return;
			enet_peer_reset(mCurPeer);
		}
		mCurPeer = NULL;
	}

	if (!mConnPeer && mClientHost)
	{
		enet_host_destroy(mClientHost);
		mClientHost = NULL;
	}
}

void TcpClient::CleanUp()
{
	if(mClientHost) 
		enet_host_destroy(mClientHost);
	mClientHost = NULL;
}

void TcpClient::AbortConnect()
{
    if (mConnPeer == NULL)
		return;

    if (mConnPeer->state != ENET_PEER_STATE_DISCONNECTED) 
		enet_peer_reset(mConnPeer);
    mConnPeer = NULL;

    if (mCurPeer != NULL) 
		return;

    enet_host_destroy(mClientHost);
    mClientHost = NULL;
}

void TcpClient::SendBuff(const void* buff, size_t len, int channel, DWORD flag)
{
	ENet::ENetPacket* packet = enet_packet_create(buff, len, flag);
	if (packet == NULL)
		return;

	SendPacket(packet, channel);
}

void TcpClient::SendPacket(ENet::ENetPacket* packet, int channel)
{
    if (mCurPeer == NULL)
	{
		// try to send the local server
		TcpServer* localServer = NetworkManager::Instance().getServer();
		if (localServer != NULL)
			localServer->ReceivePacket(packet, mClientNum, channel);
		return;
	}

	enet_peer_send(mCurPeer, channel, packet);
}

void TcpClient::ServerMessage(const Command* cmd, size_t len)
{
	switch (cmd->Type)
	{
	case CMD_SERVER_INFO:
		OnServerInfo(cmd, len);
		break;
	case CMD_WELCOME:
		mAccept = true;
		break;
	}
}

void TcpClient::ReceivePacket(int channel, ENetPacket *packet)
{
	switch (channel)
	{
	case CH_POSITION:
		break;
	case CH_MESSAGE:
		ServerMessage((const Command*)packet->data, packet->dataLength);
		break;
	case CH_FILE:
		break;
	}
}

void TcpClient::KeepAlive()
{
	if (mClientHost) 
		enet_host_service(mClientHost, NULL, 0);
}

void TcpClient::Update(float elapsedTime)
{
    if (mClientHost == NULL) 
		return;
	
    ENetEvent event;
    while (enet_host_service(mClientHost, &event, 0) > 0)
	{
		switch(event.type)
		{
		case ENET_EVENT_TYPE_CONNECT:
			Disconnect(false, false);
			mCurPeer = mConnPeer;
			mConnPeer = NULL;
			break;
		case ENET_EVENT_TYPE_RECEIVE:
			ReceivePacket(event.channelID, event.packet);
			enet_packet_destroy(event.packet);
			break;
		case ENET_EVENT_TYPE_DISCONNECT:
			if (event.peer == mConnPeer)
				AbortConnect();
			else
				Disconnect();
			break;
		default:
			break;
		}
	}
}

void TcpClient::OnServerInfo(const Command* cmd, size_t len)
{
	const ServerInfoCmd* sCmd = (const ServerInfoCmd*)cmd;
	
	ConnectInfoCmd cCmd;
	NetworkManager::Instance().ClientToServer(&cCmd, sizeof(cCmd), CH_MESSAGE);
}